﻿////////////////////////////////////////////////////////////////////////////////////////////
// File: Debug.cs                                                                         //
// Author: Mike Binnix                                                                    //
// Desc: This class manages writing data to a debug html file.  A new file is created     //
//       each time the game is ran.  Text can be formated to different colors depending   //
//       on the message being sent.                                                       //
////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace SlothEngine.Core
{    
    /// <summary>
    /// Handles writing to a html log.
    /// </summary>
    public static class Log
    {
        #region Properties
        public static FileStream file;
        public static StreamWriter writer;
        public static DateTime time;
        #endregion

        #region Methods
        /// <summary>
        /// Sets up the debug file for use at the start of a program.
        /// </summary>
        [Conditional("DEBUG")]
        public static void Setup()
        {
            try
            {
                file = new FileStream("log.html", FileMode.Create, FileAccess.Write);
                writer = new StreamWriter(file);

                writer.WriteLine("<html><body>");
            }
            catch (IOException)
            {
                MessageBox.Show("Error: Could not setup log file.", "Error", 
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Writes text line to log file.  Includes time stamp of logging.
        /// </summary>
        /// <param name="text">Text to be written to file</param>
        /// <param name="color">Color the text should be written with</param>
        [Conditional("DEBUG")]
        public static void PrintLine(string text, Color color)
        {
            time = DateTime.Now;
            string colorString = ColorToHex(color);

            writer.Write("<font color = " + colorString + ">");
            writer.Write(time.ToString("hh:mm:ss") + ":  " + text);
            writer.Write("</font><br>\n");
        }

        /// <summary>
        /// Writes text to a debug file in the default black color.  Includes time stamp.
        /// </summary>
        /// <param name="text">Text to be written to file</param>
        [Conditional("DEBUG")]
        public static void PrintLine(string text)
        {
            PrintLine(text, Color.Black);
        }

        /// <summary>
        /// Writes text line to log file.  Includes time stamp of logging.
        /// </summary>
        /// <param name="text">Text to be written to file</param>
        /// <param name="color">Color the text should be written with</param>
        [Conditional("DEBUG")]
        public static void Print(string text, Color color)
        {
            time = DateTime.Now;
            string colorString = ColorToHex(color);

            writer.Write("<font color = " + colorString + ">");
            writer.Write(text);
            writer.Write("</font>");
        }

        /// <summary>
        /// Writes text to a debug file in the default black color.
        /// </summary>
        /// <param name="text">Text to be written to file</param>
        [Conditional("DEBUG")]
        public static void Print(string text)
        {
            Print(text, Color.Black);
        }

        /// <summary>
        /// Prints the call stack for the method the command is called within.
        /// </summary>
        [Conditional("DEBUG")]
        public static void PrintStack()
        {
            StackTrace stack = new StackTrace();
            StackFrame[] stackFrame = stack.GetFrames();

            writer.WriteLine("<br>Call Stack:");
            writer.WriteLine("<table border = \"0\">");
            for (int i = 1; i < stack.FrameCount; i++)
            {
                writer.WriteLine("<tr><td>" + stackFrame[i].GetMethod().Name + "</td></tr>");
            }
            writer.WriteLine("</table>");
        }

        /// <summary>
        /// Logs the current display settings and graphics card information.
        /// </summary>
        /// <param name="manager">Applications device manager.</param>
        [Conditional("DEBUG")]
        public static void PrintHardwareInfo(GraphicsDeviceManager manager)
        {
            Print("------------------------------------------------------<br>\n");

            PrintDisplaySettings(manager);
            Print("<br>\n");
            PrintAdapterSettings(manager);

            Print("------------------------------------------------------<br>\n");
        }
        
        /// <summary>
        /// Prints machines current display settings.
        /// </summary>
        /// <param name="manager">Applications graphics device manager.</param>
        [Conditional("DEBUG")]
        public static void PrintDisplaySettings(GraphicsDeviceManager manager)
        {
            GraphicsDevice device = manager.GraphicsDevice;
            float aspectRatio = device.DisplayMode.AspectRatio;
            string aspectString = "";

            // Find the aspect ratio
            if (aspectRatio == 16.0f / 10.0f)
                aspectString = "16:10";
            else if (aspectRatio == 16.0f / 9.0f)
                aspectString = "16:9";
            else if (aspectRatio == 4.0f / 3.0f)
                aspectString = "4:3";
            else if (aspectRatio == 3.0f / 2.0f)
                aspectString = "3:2";

            Print("<b>Display Settings:</b><br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Resolution:</i> " + 
                  device.DisplayMode.Width + " x " + device.DisplayMode.Height + "<br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Refresh Rate:</i> " + 
                  device.DisplayMode.RefreshRate + "Mhz<br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Aspect Ratio:</i> " + aspectString +
                  "<br>\n");
        }

        /// <summary>
        /// Prints the machines graphics card and shader versions.
        /// </summary>
        /// <param name="manager">Applicatinos graphics device manager.</param>
        [Conditional("DEBUG")]
        public static void PrintAdapterSettings(GraphicsDeviceManager manager)
        {
            GraphicsDeviceCapabilities caps = manager.GraphicsDevice.GraphicsDeviceCapabilities;

            Print("<b>Graphics Card:</b><br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Name:</i> " +
                  GraphicsAdapter.DefaultAdapter.Description + "<br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Driver Version:</i> " +
                  GraphicsAdapter.DefaultAdapter.DriverVersion.ToString() + "<br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Pixel Shader:</i> " +
                  caps.PixelShaderVersion.ToString() + "<br>\n");
            Print("&nbsp&nbsp&nbsp&nbsp&nbsp<i>Vertex Shader:</i> " +
                  caps.VertexShaderVersion.ToString() + "<br>\n");
        }

        /// <summary>
        /// Opens debug file without overwriting data
        /// </summary>
        [Conditional("DEBUG")]
        public static void Open()
        {
            try
            {
                file = new FileStream("log.html", FileMode.OpenOrCreate, FileAccess.Write);
                writer = new StreamWriter(file);

                if (file.Length > 0)
                {
                    file.Seek(-16, SeekOrigin.End);
                }
            }
            catch (IOException)
            {
                MessageBox.Show("Error: Could not open log file.", "Error", 
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Closes streams when program is closed.
        /// </summary>
        [Conditional("DEBUG")]
        public static void Close()
        {
            writer.WriteLine("</body></html>");

            writer.Close();
            file.Close();
        }

        /// <summary>
        /// Converts a RGB color value to it's hex representation.
        /// </summary>
        /// <param name="color">Color value to convert.</param>
        /// <returns>String containing the color value in hex form.</returns>
        private static string ColorToHex(Color color)
        {
            int numberToDevide;
            int remainder = 0;
            StringBuilder hexNumber = new StringBuilder("#");

            // Loop through for BGR values
            for (int i = 0; i < 3; i++)
            {
                remainder = 0;

                // Grab correct color
                if (i == 0)
                    numberToDevide = color.B;
                else if (i == 1)
                    numberToDevide = color.G;
                else
                    numberToDevide = color.R;

                // Convert the dec value into a hex value
                for (int j = 0; j < 2; j++)
                {
                    remainder = numberToDevide % 16;
                    numberToDevide = numberToDevide / 16;

                    // Special characters for when number is >9.
                    if (remainder > 9)
                    {
                        switch (remainder)
                        {
                            case 10:
                                hexNumber.Insert(1, "A");
                                break;
                            case 11:
                                hexNumber.Insert(1, "B");
                                break;
                            case 12:
                                hexNumber.Insert(1, "C");
                                break;
                            case 13:
                                hexNumber.Insert(1, "D");
                                break;
                            case 14:
                                hexNumber.Insert(1, "E");
                                break;
                            case 15:
                                hexNumber.Insert(1, "F");
                                break;
                        }
                    }
                    else
                    {
                        hexNumber.Insert(1, remainder.ToString());
                    }
                }
            }

            return hexNumber.ToString();
        }
        #endregion
    }
}
